package com.warm.micro.consumer.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import com.warm.micro.api.feign.ProviderFeign;
import com.warm.micro.common.exception.CommonException;
import com.warm.micro.common.vo.Resp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @Date: 2020/5/8 15:09
 * @Author: LChuang
 * @Description:
 */
@Slf4j
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Value("${server.port}")
    private String port;

    /**
     * RestConfig 配置了 Ribbon 相关的配置
     */
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ProviderFeign providerFeign;

    /**  使用 RestTemplate + Ribbon 实现微服务间的调用 */
    @RequestMapping(value = "/native/ribbon", method = RequestMethod.GET)
    public Resp<String> nativeRibbon(String msg){
        log.error("consumer.native.ribbon() ========== {}", msg);
        String uri = "/provider/hello?msg=" + msg;
        // 使用了 @LoadBalanced 后 就可以直接通过服务名调用
        String url = "http://micro-provider" + uri;
        Resp resp = restTemplate.getForObject(url, Resp.class);
        return Resp.success("Consumer-" + port + " get msg : ===> " + resp.getData());
    }

    /** 使用 Feign 实现微服务间的调用 */
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public Resp<String> hello(String msg){
        log.error("consumer.hello() ========== {}", msg);
        Resp resp = providerFeign.hello(msg);
        return Resp.success("Consumer-" + port + " get msg : ===> " + resp.getData());
    }

    /**
     *  降级是指，当请求超时、资源不足等情况发生时进行服务降级处理，不调用真实服务逻辑，
     *  而是使用快速失败（fallback）方式直接返回一个托底数据，保证服务链条的完整，避免服务雪崩。
     */
    /**  服务熔断 -- 方式1*/
    @HystrixCommand(fallbackMethod = "fuse",
            commandProperties = {
                    // 当服务发生问题时，使用技术手段隔离请求，保证服务调用链的完整。隔离分为线程池隔离和信号量隔离两种实现方式。
                    @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value= "1000"),
                    // 默认20个;10ms内请求数大于20个时就启动熔断器，当请求符合熔断条件时将触发getFallback()。
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
                    // 请求错误率大于50%时就熔断，然后for循环发起请求，当请求符合熔断条件时将触发getFallback()。
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                    // 默认5秒;熔断多少秒后去尝试请求
                    @HystrixProperty(name= HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value="5000")
            },
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "1"),
                    @HystrixProperty(name = "maxQueueSize", value = "10"),
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "1000"),
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "8"),
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1500")
            },ignoreExceptions = CommonException.class)
    @RequestMapping(value = "/hystrix1", method = RequestMethod.GET)
    public Resp<String> hystrix1(String msg){
        log.error("consumer.hello() ========== {}", msg);
        try {
            // 制造熔断 timeoutInMilliseconds = 1000
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Resp resp = providerFeign.hello(msg);
        return Resp.success("Consumer-" + port + " get msg : ===> " + resp.getData());
    }

    /** 熔断处理 -- 是业务处理的保底方案，尽可能保证这个保底方案一定能成功 */
    public Resp<String> fuse(String msg){
        log.error("consumer.hystrix() 熔断测试 ========== {}", msg);
        return Resp.success("provider.hello() 注解 -- 熔断" + msg);
    }

    /**  服务熔断 -- 方式2, 3 通过降级类 / 降级工厂 实现*/
    @RequestMapping(value = "/hystrix2", method = RequestMethod.GET)
    public Resp<String> hystrix2(String msg){
        log.error("consumer.hello() ========== {}", msg);
        // 制造熔断 -- 关闭 micro-provider
        Resp resp = providerFeign.hello(msg);
        return Resp.success("Consumer-" + port + " get msg : ===> " + resp.getData());
    }

    /**
     * 注解属性:
     * CIRCUIT_BREAKER_ENABLED
     * "circuitBreaker.enabled";
     * # 是否开启熔断策略。默认值为true。
     *
     * CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD
     * "circuitBreaker.requestVolumeThreshold";
     * # 10ms内，请求并发数超出则触发熔断策略。默认值为20。
     *
     * CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS
     * "circuitBreaker.sleepWindowInMilliseconds";
     * # 当熔断策略开启后，延迟多久尝试再次请求远程服务。默认为5秒。
     *
     * CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE
     * "circuitBreaker.errorThresholdPercentage";
     * # 10ms内，出现错误的请求百分比达到限制，则触发熔断策略。默认为50%。
     *
     * CIRCUIT_BREAKER_FORCE_OPEN
     * "circuitBreaker.forceOpen";
     * # 是否强制开启熔断策略。即所有请求都返回fallback托底数据。默认为false。
     *
     * CIRCUIT_BREAKER_FORCE_CLOSED
     * "circuitBreaker.forceClosed";
     * # 是否强制关闭熔断策略。即所有请求一定调用远程服务。默认为false。
     */
}
